import argparse
import time

import torch.nn as nn
import torch.optim as optim
from torch.optim.lr_scheduler import CosineAnnealingLR, StepLR
from model import M_Net
from dataset import weatherDataset
from torch.utils.data import DataLoader
import torch

def cal_loss(prd, truth):
    return torch.nn.MSELoss(reduction='mean')(prd, truth)

def train(args):

    trainDataset = weatherDataset(args.data_path,'train')
    testDataset = weatherDataset(args.data_path,'test')

    train_loader = DataLoader(trainDataset, num_workers=8,batch_size=1, shuffle=True, drop_last=True)
    test_loader = DataLoader(testDataset, num_workers=8,batch_size=1, shuffle=True, drop_last=True)

    model = M_Net(60)

    opt = optim.SGD(model.parameters(), lr=args.lr*100, momentum=args.momentum, weight_decay=1e-4)
    scheduler = CosineAnnealingLR(opt, args.epochs, eta_min=1e-3)
    criterion = cal_loss

    for epoch in range(args.epochs):
        train_loss = 0
        count = 0.0
        for data,label in train_loader:
            batch_size = data.size()[0]
            opt.zero_grad()
            out, side_5, side_6, side_7, side_8, out1, out2, out3, out4 = model(data)

            loss = cal_loss(out, label)
            loss.backward()
            opt.step()

            count += batch_size
            train_loss += loss

        scheduler.step()
        print("Train %d, loss: %.6f\n" %(epoch, train_loss/count))

        with torch.no_grad():
            test_loss = 0
            count = 0.0
            for data,label in test_loader:
                batch_size = data.size()[0]
                opt.zero_grad()
                out, side_5, side_6, side_7, side_8, out1, out2, out3, out4 = model(data)

                loss = cal_loss(out, label)

                count += batch_size
                test_loss += loss
            print("Test %d, loss: %.6f\n" %(epoch, test_loss/count))


def get_args():
    parser = argparse.ArgumentParser(description='weather Analysis')
    parser.add_argument('--exp_name', type=str, default='exp', metavar='N',
                        help='Name of the experiment')
    parser.add_argument('--dataset', type=str, default='modelnet40', metavar='N',
                        choices=['modelnet40'])
    parser.add_argument('--batch_size', type=int, default=32, metavar='batch_size',
                        help='Size of batch)')
    parser.add_argument('--test_batch_size', type=int, default=32, metavar='batch_size',
                        help='Size of batch)')
    parser.add_argument('--epochs', type=int, default=250, metavar='N',
                        help='number of episode to train ')
    parser.add_argument('--use_sgd', type=bool, default=True,
                        help='Use SGD')
    parser.add_argument('--lr', type=float, default=0.001, metavar='LR',
                        help='learning rate (default: 0.001, 0.1 if using sgd)')
    parser.add_argument('--momentum', type=float, default=0.9, metavar='M',
                        help='SGD momentum (default: 0.9)')
    parser.add_argument('--scheduler', type=str, default='cos', metavar='N',
                        choices=['cos', 'step'],
                        help='Scheduler to use, [cos, step]')
    parser.add_argument('--no_cuda', type=bool, default=False,
                        help='enables CUDA training')
    parser.add_argument('--resume', type=bool, default=False,
                        help='enables re training')
    parser.add_argument('--seed', type=int, default=1, metavar='S',
                        help='random seed (default: 1)')
    parser.add_argument('--eval', type=bool,  default=False,
                        help='evaluate the model')
    parser.add_argument('--num_points', type=int, default=1024,
                        help='num of points to use')
    parser.add_argument('--dropout', type=float, default=0.5,
                        help='initial dropout rate')
    parser.add_argument('--emb_dims', type=int, default=1024, metavar='N',
                        help='Dimension of embeddings')
    parser.add_argument('--k', type=int, default=20, metavar='N',
                        help='Num of nearest neighbors to use')
    parser.add_argument('--model_path', type=str, default='', metavar='N',
                        help='Pretrained model path')
    parser.add_argument('--data_path', type=str, default='', metavar='N',
                        help='Data path')
    return parser.parse_args()

if __name__ == '__main__':
    args = get_args()
    train(args)
